In the latter half of the twentieth century, there began a phenomenon known as the information revolution. While the information revolution is a historical development broader in scope than any one event or machine, no single device has come to represent the information revolution more than the digital electronic computer. The development of computer systems has surely been a revolution. Each year, computer systems grow faster, store more data, and provide more applications to their users.
A modern computer system typically comprises one or more central processing units (CPU) and supporting hardware necessary to store, retrieve and transfer information, such as communication buses and memory. It also includes hardware necessary to communicate with the outside world, such as input/output controllers or storage controllers, and devices attached thereto such as keyboards, monitors, tape drives, disk drives, communication lines coupled to a network, etc. The CPU or CPUs are the heart of the system. They execute the instructions which comprise a computer program and directs the operation of the other system components.
From the standpoint of the computer's hardware, most systems operate in fundamentally the same manner. Processors are capable of performing a limited set of very simple operations, such as arithmetic, logical comparisons, and movement of data from one location to another. But each operation is performed very quickly. Sophisticated software at multiple levels directs a computer to perform massive numbers of these simple operations, enabling the computer to perform complex tasks. What is perceived by the user as a new or improved capability of a computer system is made possible by performing essentially the same set of very simple operations, but using software having enhanced function, along with faster hardware.
In the very early history of the digital computer, computer programs which instructed the computer to perform some task were written in a form directly executable by the computer's processor. Such programs were very difficult for a human to write, understand and maintain, even when performing relatively simple tasks. As the number and complexity of such programs grew, this method became clearly unworkable. As a result, alternate forms of creating and executing computer software were developed.
The evolution of computer software has led to the creation of sophisticated software development environments. These environments typically contain a range of tools for supporting the development of software in one or more high-level languages. For example, interactive source editors support the initial generation of source code by a developer. Source databases may support collections of source modules or source objects, which serve as the component parts of software applications. Front-end compiler/debuggers perform simple semantic verification of the source and reduction to a standard form. Back-end or optimizing compilers generate machine executable object code from the standard form, and may optimize the performance of this code using any of various optimization techniques. Build utilities assemble multiple object code modules into fully functioning computer programs.
Among the tools available in many such programming development environments are a range of diagnostic and debug tools for analyzing the execution of computer programming code, particularly compiled code. A common analytical technique is the tracing of code execution using one or more tracepoints. A tracepoint is a location in a code sequence at which certain state data is saved for later analysis. Upon encountering a tracepoint during execution, the corresponding state data in saved in a designated area of memory referred to as a trace buffer, and the program continues execution. The contents of the trace buffer can later be examined to analyze program behavior, determine the cause of program errors, and so forth.
One problem with tracing is that it tends to produce a very large volume of traced state data. In many environments, it is necessary for various reasons of memory and storage management to limit the amount of trace data which is saved, either in memory or storage. Even where not strictly required by the environment, it is often desirable to limit the amount of trace data saved, if for no other reason than to reduce the difficulty of subsequent analysis.
Trace data can be limited in a variety of ways. The user may limit the number of tracepoints and/or the amount of state data saved at each trace point to reduce the total volume of collected data. Alternatively or additionally, a trace buffer may hold a limited volume of most recently gathered trace data, older data being overwritten as new trace data is accumulated. As a further alternative, tracepoints can be conditionally enabled, so that upon encountering a tracepoint, data is only collected if some condition is met.
Properly used, these and other conventional techniques provide the program developer with a range of capabilities for tracing the behavior of a program. However, there are still various programming circumstances which are difficult to adequately trace using conventional trace tools.
One such circumstance is a situation in which a repetitively called procedure produces an unexpected result on infrequent calls of the procedure. For example, a calling procedure P contains a loop in which a called procedure Q is invoked, the loop iterating many times. On infrequent occasions, the called procedure Q produces an unexpected result, as by branching to or calling an infrequently used code path. In such a circumstance, it may be desirable to understand the behavior of procedure P, or other procedures which called P, which produced the unexpected result in Q. However, if a trace buffer has limited size, it is possible that, when the critical point is encountered, the trace buffer will be filled with data from multiple invocations of procedure Q, effectively overwriting any previously traced data from procedure P or the procedures which called it. Alternatively, the need to collect large volumes of trace data for each invocation of Q, although many produce no abnormal result, can affect the number of tracepoints and volume to state data at each point which the programmer decides to collect.
As programs grow in size and complexity, the difficulty of isolating trace data to significant conditions and eliminating from the trace that data which is not significant to the problem is likely to increase. A need exists for a more effective trace tools which will assist the programmer selectively cull relevant trace data from the execution of a program.